import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from arch import arch_model
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from keras.models import Sequential
from keras.layers import LSTM, Dense

# Load all datasets and combine them
file_paths = [
    "C:/Users/HP/Desktop/quantitative_easing_financial_data.csv",
    "C:/Users/HP/Desktop/processed_brexit_features.csv",
    "C:/Users/HP/Desktop/processed_covid_19_crisis_features.csv",
    "C:/Users/HP/Desktop/processed_dodd_frank_act_features.csv",
    "C:/Users/HP/Desktop/processed_eurozone_debt_crisis_features.csv",
    "C:/Users/HP/Desktop/processed_global_financial_crisis_features.csv",
    "C:/Users/HP/Desktop/processed_quantitative_easing_features.csv"
]

dfs = []
for file_path in file_paths:
    df = pd.read_csv(file_path)
    dfs.append(df)

# Combine datasets with unique suffixes to avoid duplicate columns
df_combined = dfs[0]
for i, df in enumerate(dfs[1:], start=1):
    df_combined = pd.merge(
        df_combined,
        df,
        on='Timestamp',
        how='outer',
        suffixes=(None, f'_{i}')
    )

# Ensure proper sorting and handle missing values
df_combined['Timestamp'] = pd.to_datetime(df_combined['Timestamp'])
df_combined.sort_values('Timestamp', inplace=True)
df_combined.fillna(method='ffill', inplace=True)

# Drop duplicate columns created during merging
df_combined = df_combined.loc[:, ~df_combined.columns.duplicated()]

# Feature: Calculate returns
df_combined['Stock_Index'] = df_combined['Stock_Index'].astype(float)  # Ensure numeric
returns = df_combined['Stock_Index'].pct_change()
returns = returns.replace([np.inf, -np.inf], np.nan).dropna()

# ---- GARCH Model ----
print("Fitting GARCH Model...")
garch_model = arch_model(returns, vol='Garch', p=1, q=1)
garch_fit = garch_model.fit(disp="off")

# Plot GARCH Conditional Volatility
volatility = garch_fit.conditional_volatility
plt.figure(figsize=(10, 6))
plt.plot(volatility, label='GARCH Conditional Volatility')
plt.title('GARCH Conditional Volatility')
plt.legend()
plt.show()

# Print GARCH summary
print(garch_fit.summary())

# ---- LSTM Model ----
print("Fitting LSTM Model...")

# Normalize the data
scaler = MinMaxScaler(feature_range=(0, 1))
df_combined['Stock_Index_Normalized'] = scaler.fit_transform(df_combined['Stock_Index'].values.reshape(-1, 1))

# Prepare data for LSTM
sequence_length = 60
data = df_combined['Stock_Index_Normalized'].values
X, y = [], []
for i in range(sequence_length, len(data)):
    X.append(data[i-sequence_length:i])
    y.append(data[i])
X, y = np.array(X), np.array(y)

# Reshape for LSTM
X = X.reshape(X.shape[0], X.shape[1], 1)

# Train-test split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Define LSTM Model
model = Sequential([
    LSTM(64, return_sequences=True, input_shape=(X_train.shape[1], 1)),
    LSTM(64, return_sequences=False),
    Dense(32, activation='relu'),
    Dense(1)
])
model.compile(optimizer='adam', loss='mean_squared_error')

# Train the LSTM model
history = model.fit(X_train, y_train, batch_size=32, epochs=50, validation_data=(X_test, y_test))

# Plot training history
plt.figure(figsize=(10, 6))
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('LSTM Training and Validation Loss')
plt.legend()
plt.show()

# Predictions
y_pred = model.predict(X_test)
y_pred = scaler.inverse_transform(y_pred.reshape(-1, 1))
y_test = scaler.inverse_transform(y_test.reshape(-1, 1))

# Plot predictions vs actual values
plt.figure(figsize=(10, 6))
plt.plot(y_test, label='Actual', color='blue')
plt.plot(y_pred, label='Predicted', color='orange')
plt.title('LSTM Predictions vs Actual Values')
plt.legend()
plt.show()

# ---- Results ----
print("GARCH Model Summary:")
print(garch_fit.summary())

print("\nLSTM Model Completed:")
print(f"Training Loss: {history.history['loss'][-1]}")
print(f"Validation Loss: {history.history['val_loss'][-1]}")
